function WSClient(url, method, ns) {

    var soapHeader = {};
    var errorCode = 0;
    var errorReason;
    var _url = url;
    var _method = method;
    var _ns = ns;

    this.requestBody = "";

    function createXmlHttpRequestObject() {
        var xmlHttp;

        try {
            xmlHttp = new XMLHttpRequest();
        }
        catch(e)
        {
            var XmlHttpVersions = new Array("MSXML2.XMLHTTP.6.0",
                                            "MSXML2.XMLHTTP.5.0",
                                            "MSXML2.XMLHTTP.4.0",
                                            "MSXML2.XMLHTTP.3.0",
                                            "MSXML2.XMLHTTP",
                                            "Microsoft.XMLHTTP");
            for (var i = 0; i < XmlHttpVersions.length && !xmlHttp; i++)
            {
                try {
                    xmlHttp = new ActiveXObject(XmlHttpVersions[i]);
                }
                catch(e) {
                    SiteNote.Debug.error("[WSClient] " + e.toString());
                }
            }
        }
        if (!xmlHttp)
        {
            SiteNote.Debug.error("[WSClient] Error has occured during XMLHttpRequest object creation.")
        }
        else
        {
            return xmlHttp;
        }
    };

    var xmlHttpRequest = createXmlHttpRequestObject();

    this.open = function(url, method, ns)
    {
        _url = url;
        if (method)
        {
            _method = method;
        }

        if (ns)
        {
            _ns = ns;
        }
    };

    this.getMethod = function() {
        if (!_method)
        {
            _method = "POST";
        }

        switch(_method.toUpperCase())
        {
            case "GET":
            case "POST":
                return _method.toUpperCase();
            default:
                throw "[WSClient] Unexpected method '" + _method + "' of the http request."
        }
    };

    function buildSoap12Body(method, args)
    {
        var result = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
        result += "<soap12:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soap12=\"http://www.w3.org/2003/05/soap-envelope\">\n";
        result += buildSoap12Header();
        result += "  <soap12:Body>\n";
        result += "    <" + method + " xmlns=\"" + _ns + "\">\n";
        if (args)
        {
            for (var name in args)
            {
                result += "      <" + name + ">";
                result += encodeXML(args[name]);
                result += "</" + name + ">\n"
            }
        }
        result += "    </" +method + ">\n";
        result += "  </soap12:Body>\n";
        result += "</soap12:Envelope>\n";
        return result;
    };

    function buildSoap12Header()
    {
        var result = "";
        if (soapHeader)
        {
            result = "  <soap12:Header>\n";
            for (var p in soapHeader)
            {
                result += "<" + p + " xmlns=\"" +_ns + "\">\n";
                var o = soapHeader[p];
                if (o)
                {
                    for (var pp in o)
                    {
                        result += "<" + pp + ">" + o[pp] + "</" + pp + ">\n"
                    }
                }
                result += "</" + p + ">\n";
            }
            result += "  </soap12:Header>\n"; 
        }
        return result;
    };

    function encodeXML(xml)
    {
        var e = document.createElement('div');
        var text = document.createTextNode(xml);
        e.appendChild(text);
        return e.innerHTML;
    };

    this.setSoapHeader = function(property, args)
    {
        soapHeader[property] = args;
    };


    this.invoke = function(methodName, args)
    {
        try
        {
            if (xmlHttpRequest.readyState == 4 || xmlHttpRequest.readyState == 0)
            {
                xmlHttpRequest.open(this.getMethod(), _url, false);
                xmlHttpRequest.setRequestHeader("Content-Type", "application/soap+xml; charset=utf-8");
                this.requestBody = buildSoap12Body(methodName, args);
                xmlHttpRequest.send(this.requestBody);

                var xmlDocument = xmlHttpRequest.responseXML;
                if (!xmlDocument || (xmlDocument.xml == "" && xmlDocument.responseText != ""))
                {
                    xmlDocument = new ActiveXObject("Msxml2.DOMDocument.6.0");
                    xmlDocument.async = false;
                    xmlDocument.resolveExternals = true;
                    xmlDocument.preserveWhiteSpace = true;
                    xmlDocument.loadXML(xmlHttpRequest.responseText);
                }
                xmlDocument.setProperty("SelectionNamespaces", "xmlns:soap=\"http://www.w3.org/2003/05/soap-envelope\" xmlns:sa=\"" + _ns + "\"");
                handleError(xmlDocument);

                if (this.getErrorCode() != 0)
                {
                    return null;
                }

                var result = xmlDocument.selectSingleNode("//soap:Body/sa:" + methodName + "Response/sa:" + methodName + "Result");
                if (!result)
                {
                    throw "WSClient: Parse error: could not get the result";
                }
                return nodeToObj(result);
            }
            else
            {
                throw "XmlHTTPRequest object is not ready to send request."
            }
        }
        catch(e)
        {
            SiteNote.Debug.error("[WSClient] " + e.toString());
        }
    };

    function nodeToObj(node)
    {
        var json = nodeToJSON(node);
        if (!json || json.length == 0)
        {
            return null;
        }

        switch(json.substring(0,1))
        {
            case("{"):
                return eval("(" + json + ")");
            case("\""):
                return json.replace(/\\\"/g, "\"").slice(1, -1);
            default:
                throw "Unexpected symbol character in JSON";
        }
    }

    function nodeToJSON(node)
    {
        var result = "";
        var count = 0;
        if (node && (count = node.childNodes.length) > 0)
        {
            for(var i = 0; i < count; i++)
            {
                var node = node.childNodes[i];
                switch(node.nodeType)
                {
                    case(1):
                            result += "{";
                            result += node.nodeName;
                            result += ":";
                            result += nodeToJSON(node, true);
                            result += "}";
                    break;
                    case(3):
                           result += "\"" + node.text.replace(/\"/g, "\\\"") + "\"";
                    break;
                }
            }
        }
        return result;
    };

    function handleError(xml)
    {
        if (xml) {
            if ((errorCode = xml.selectNodes("//soap:Fault").length) > 0)
            {
                var reason = xml.selectSingleNode("//soap:Fault/soap:Reason/soap:Text");
                if (reason)
                {
                    errorReason = reason.text;
                    if (errorReason.indexOf("CrossFactory.SiteNote.Security.AuthenticationException") != -1)
                    {
                        errorReason = "Call is not authenticated";
                    }
                }
            }
            else
            {
                errorReason = "";
            }
        }
    };

    this.getErrorReason = function()
    {
        return errorReason;
    };

    this.getErrorCode = function()
    {
        return errorCode;
    };

};






